/**
 * The Tasks class contains a list of tasks
 */
package tms.model;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author Rolf Schmidt
 * @version 1.0
 * 
 */
public class Project extends AbstractModelObject implements PropertyChangeListener {

	/**
	 * the only instance of the class
	 */
	private static Project s_instance = null;

	/**
	 * getter of the only instance of the class
	 * 
	 * @return the only instance of the class
	 */
	public static Project getInstance() {
		if (s_instance == null)
			s_instance = new Project();
		return s_instance;
	}

	/**
	 * a list of people
	 */
	private PeopleList peopleList;

	/**
	 * a tree of tasks
	 */
	private TaskTree taskTree;

	/**
	 * a list of dependencies between tasks
	 */
	private DependencyManager dependencyManager;

	/**
	 * private default constructor
	 */
	private Project() {
		peopleList = PeopleList.getInstance();
		taskTree = TaskTree.getInstance();
		dependencyManager = DependencyManager.getInstance();

		// register a listener to update the peopleList when the taskTree
		// changes
		taskTree.addPropertyChangeListener(this);
		dependencyManager.addPropertyChangeListener(this);
	}

	/**
	 * @return the taskTree
	 */
	public TaskTree getTaskTree() {
		return taskTree;
	}

	/**
	 * @return the dependencyManager
	 */
	public DependencyManager getDependencyManager() {
		return dependencyManager;
	}

	/**
	 * @return the dependencyMap
	 */
	public TreeMap<Integer, Integer> getDependencyMap() {
		return dependencyManager.getDependencyMap();
	}

	/**
	 * @return people the list of people assigned to the tasks
	 */
	public List<Person> getPeopleList() {
		return peopleList.getPeopleList();
	}

	private void setPeopleList() {
		TreeMap<String, Person> personMap = new TreeMap<String, Person>();

		// iterate through all the tasks and add people to the map
		// (remember: the map does not allow duplicates)
		for (Task task : taskTree.getFlatTasklist()) {
			String name = task.getPerson();
			if (!personMap.containsKey(name)) {
				Person person = new Person();
				person.setName(name);
				personMap.put(name, person);
			}
			Person person = personMap.get(name);
			person.addTask(task);
		}
		peopleList.clear();
		for (Map.Entry<String, Person> entry : personMap.entrySet()) {
			peopleList.addPerson(entry.getValue());
		}
		firePropertyChange("peopleList", null, this.peopleList);
	}

	/**
	 * @return the tasks (i.e. only top-level)
	 */
	public ArrayList<Task> getTasklist() {
		ArrayList<Task> allTasks = new ArrayList<Task>();

		for (Task task : taskTree.getTasklist()) {
			allTasks.add(task);
		}
		return allTasks;
	}

	/**
	 * @return the tasks and subtasks in a flat list for display in table
	 */
	public ArrayList<Task> getFlatTasklist() {
		return this.taskTree.getFlatTasklist();
	}

	/**
	 * @return the top-level tasks in a flat list (i.e. subtasks are excluded)
	 */
	public ArrayList<Task> getTopLevelTasklist() {
		return taskTree.getTopLevelTasklist();
	}

	/**
	 * add a task to the list
	 * 
	 * @param task
	 *            the task to add
	 */
	public Task addTask() {
		Task task = taskTree.addTask();
		task.addPropertyChangeListener(this);
		task.addPropertyChangeListener(dependencyManager);
		firePropertyChange("project", null, this);

		return task;
	}

	/**
	 * add a subtask to parent
	 * 
	 * @param parent
	 *            the task to which a subtask will be added
	 */
	public Task addSubtask(Task parent) {
		Task subtask = taskTree.addSubtask(parent);
		subtask.addPropertyChangeListener(this);
		subtask.addPropertyChangeListener(dependencyManager);
		firePropertyChange("project", null, this);

		return subtask;
	}

	/**
	 * remove a task from the list
	 * 
	 * @param task
	 *            the task to remove
	 */
	public void removeTask(Task task) {
		taskTree.removeTask(task);
		firePropertyChange("project", null, this);
	}

	/**
	 * clear the list
	 */
	public void clear() {
		taskTree.clear();
		peopleList.clear();
		firePropertyChange("project", null, this);
	}

	/*
	 * loadProjectFile populates the taskTree instance with tasks that are read
	 * from an input file.
	 * If the filename is invalid (empty string, null, or does not exist) or if
	 * the format and content of the file is invalid, no tasks will be added to
	 * the taskList and false will be return.
	 * 
	 * 
	 * @param filename
	 * the path to the input file that contains the tasks to be
	 * added to the project
	 */
	public Boolean loadProjectFile(String fileName) {
		if (getTaskTree().loadXMLFile(fileName)) {
			// the peopleList will be notified of the change and update automatically
			peopleList.clear();
			dependencyManager.clear();
			for (Task task : taskTree.getTopLevelTasklist()) {
				// add a property change listener
				task.addPropertyChangeListener(this);
				task.addPropertyChangeListener(dependencyManager);
				// add dependencies if necessary
				if (task.getBeforeId() >= 0) {
					Task second = taskTree.getTask(task.getBeforeId());
					dependencyManager.addDependency(task, second);
				}
			}

			firePropertyChange("project", null, this);
			return true;
		}
		return false;
	}

	/**
	 * saves the project to an XML file
	 * 
	 * @param fileName
	 *            path to the file where the content of the people list will be
	 *            saved
	 * @return true if the file was saved successfully, false otherwise
	 */
	public boolean saveProjectFile(String selectedFile) {
		return taskTree.saveXMLFile(selectedFile);
	}

	/**
	 * saves the people list to a CSV file
	 * 
	 * @param fileName
	 *            path to the file where the content of the people list will be
	 *            saved
	 * @return true if the file was saved successfully, false otherwise
	 */
	public Boolean savePeopleList(String fileName) {
		return peopleList.saveCSVFile(fileName);
	}

	/**
	 * save the task list to a CSV file
	 * 
	 * @param fileName
	 *            path to the file where the content of the task list will be
	 *            saved
	 * @return true if the file was saved successfully, false otherwise
	 */
	public Boolean saveTaskList(String fileName) {
		return taskTree.saveCSVFile(fileName);
	}

	@Override
	/**
	 * this method gets called whenever there is a property change
	 * 
	 *  @param evt a PropertyChangeEvent object describing the event source and the property that has changed.
	 */
	public void propertyChange(PropertyChangeEvent evt) {
		// update the peopleList when the taskTree changes
		setPeopleList();

		// fire all necessary property changes
		firePropertyChange("project", null, this);
		firePropertyChange("tasktree", null, this.getTaskTree());
		firePropertyChange("tasklist", null, this.getTasklist());
		firePropertyChange("flatTasklist", null, this.getFlatTasklist());
		firePropertyChange("peopleList", null, this.getPeopleList());
		firePropertyChange("dependencyMap", null, this.getDependencyManager());
	}

}
